import * as THREE from 'three';
import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { MeshBVH, StaticGeometryGenerator } from 'three-mesh-bvh';

/**
 * 玩家控制器类 - 处理角色移动、碰撞检测和相机控制
 */
export default class PlayerController {
  constructor(scene, camera, rendererDomElement, controls, options) {
    // ========== 初始化基础属性 ==========
    this.scene = scene;                      // Three.js场景对象
    this.camera = camera;                    // 相机对象
    this.rendererDomElement = rendererDomElement; // 渲染器DOM元素
    this.controls = controls;               // 轨道控制器

    // ========== 玩家参数配置 ==========
    this.params = {
      firstPerson: false,       // 是否第一人称视角模式
      displayCollider: false,    // 是否显示碰撞体线框
      displayBVH: false,        // 是否显示BVH结构（调试用）
      visualizeDepth: 10,       // BVH可视化深度级别
      gravity: -30,             // 重力加速度（单位：m/s²）
      playerSpeed: 10,          // 玩家移动速度
      physicsSteps: 5,          // 每帧物理更新次数（提高稳定性）
      ...options                // 合并自定义配置
    };

    // ========== 玩家状态 ==========
    this.player = null;         // 玩家角色网格对象
    this.collider = null;       // 环境碰撞体网格
    this.environment = null;    // 环境模型容器组
    this.visualizer = null;     // BVH可视化辅助对象

    this.playerIsOnGround = false;           // 是否在地面状态标记
    this.playerVelocity = new THREE.Vector3(); // 当前速度向量
    this.upVector = new THREE.Vector3(0, 1, 0); // 世界坐标系上方向

    // ========== 输入状态 ==========
    this.fwdPressed = false;    // 前进键(W)状态
    this.bkdPressed = false;    // 后退键(S)状态
    this.lftPressed = false;    // 左移键(A)状态
    this.rgtPressed = false;    // 右移键(D)状态

    // ========== 临时变量 ==========
    // （避免频繁创建新对象，优化性能）
    this.tempVector = new THREE.Vector3();
    this.tempVector2 = new THREE.Vector3();
    this.tempBox = new THREE.Box3();
    this.tempMat = new THREE.Matrix4();
    this.tempSegment = new THREE.Line3();

    // ========== 初始化 ==========
    this.initPlayer();      // 创建玩家角色
    this.initControls();    // 设置控制器参数
    this.setupEventListeners(); // 绑定输入事件
  }

  /**
   * 初始化玩家角色模型和碰撞体
   */
  initPlayer() {
    // 创建玩家模型 - 使用圆角长方体几何体
    this.player = new THREE.Mesh(
      new RoundedBoxGeometry(
        1.0,   // 宽度
        2.0,   // 高度（玩家身高）
        1.0,   // 深度
        10,    // 分段数
        0.5    // 圆角半径
      ),
      new THREE.MeshStandardMaterial() // 标准材质
    );

    // 将几何体沿Y轴下移0.5单位，使底部对齐原点
    this.player.geometry.translate(0, -0.5, 0);

    // 设置胶囊碰撞体参数（用于物理检测）
    this.player.capsuleInfo = {
      radius: 0.5, // 碰撞胶囊半径
      segment: new THREE.Line3(
        new THREE.Vector3(0, 0, 0),     // 胶囊顶部（玩家头顶）
        new THREE.Vector3(0, -1.0, 0.0)  // 胶囊底部（玩家脚部）
      )
    };

    // 阴影设置
    this.player.castShadow = true;     // 允许投射阴影
    this.player.receiveShadow = true;   // 允许接收阴影
    this.player.material.shadowSide = 2; // 双面阴影计算

    // 将玩家添加到场景
    this.scene.add(this.player);

    // 重置初始状态
    this.reset();
  }
  targetFirstPerson (value) { 
    this.params.firstPerson = value;
  }
  /**
   * 初始化控制器参数
   */
  initControls() {
    // 设置轨道控制器限制参数
    this.controls.maxPolarAngle = Math.PI / 2; // 最大俯仰角（限制不能看地下）
    this.controls.minDistance = 1;    // 最小相机距离
    this.controls.maxDistance = 20;   // 最大相机距离
  }

  /**
   * 设置键盘事件监听
   */
  setupEventListeners() {
    // 键盘按下事件处理
    const handleKeyDown = (e) => {
      switch (e.code) {
        case 'KeyW': this.fwdPressed = true; break; // W键 - 前进
        case 'KeyS': this.bkdPressed = true; break; // S键 - 后退
        case 'KeyD': this.rgtPressed = true; break; // D键 - 右移
        case 'KeyA': this.lftPressed = true; break; // A键 - 左移
        case 'Space': // 空格键 - 跳跃
          if (this.playerIsOnGround) {
            this.playerVelocity.y = 10.0;      // 设置垂直速度
            this.playerIsOnGround = false;     // 标记为离地状态
          }
          break;
      }
    };

    // 键盘释放事件处理
    const handleKeyUp = (e) => {
      switch (e.code) {
        case 'KeyW': this.fwdPressed = false; break;
        case 'KeyS': this.bkdPressed = false; break;
        case 'KeyD': this.rgtPressed = false; break;
        case 'KeyA': this.lftPressed = false; break;
      }
    };

    // 绑定事件监听器
    window.addEventListener('keydown', handleKeyDown.bind(this));
    window.addEventListener('keyup', handleKeyUp.bind(this));

    // 保存引用以便后续清理
    this._keyDownHandler = handleKeyDown;
    this._keyUpHandler = handleKeyUp;
  }

  /**
   * 设置环境模型并生成碰撞体
   * @param {THREE.Group} gltfScene 从GLTF加载的场景组
   */
  setEnvironment(gltfScene) {
    // 计算场景包围盒并居中
    const box = new THREE.Box3();
    box.setFromObject(gltfScene); // 计算场景包围盒
    // box.getCenter(gltfScene.position).negate(); // 获取中心点并取反（居中处理）
    gltfScene.updateMatrixWorld(true); // 更新世界变换矩阵

    // 创建环境容器组
    this.environment = new THREE.Group();

    // 使用静态几何体生成器合并所有模型
    const staticGenerator = new StaticGeometryGenerator(gltfScene);
    staticGenerator.attributes = ['position']; // 仅合并顶点位置数据

    // 生成合并后的几何体
    const mergedGeometry = staticGenerator.generate();
    
    // 为几何体创建BVH（边界体积层次结构）加速碰撞检测
    mergedGeometry.boundsTree = new MeshBVH(mergedGeometry);

    // 创建碰撞体网格（调试用线框显示）
    this.collider = new THREE.Mesh(
      mergedGeometry,
      new THREE.MeshBasicMaterial({
        wireframe: true,   // 线框模式
        opacity: 0.5,      // 半透明
        transparent: true, // 启用透明度
        color: 0x00ff00   // 绿色线框
      })
    );
    this.collider.visible = this.params.displayCollider; // 根据参数控制显示

    // 将碰撞体和场景添加到Three.js场景
    this.scene.add(this.collider);
    this.scene.add(gltfScene);
  }

  /**
   * 重置玩家位置和状态
   */
  reset() {
    
    this.playerVelocity.set(0, 10, 0);       // 重置速度（初始Y轴速度）
    this.player.position.set(100, 30, -25);       // 设置初始位置（空中）

    // 调整相机位置使其跟随玩家
    this.camera.position.sub(this.controls.target);
    this.controls.target.copy(this.player.position);
    this.camera.position.add(this.player.position);
    this.controls.update(); // 强制更新控制器

    this.controls.target.set(-100, 90, 65)
  }

  /**
   * 每帧更新
   * @param {number} delta 帧时间差（秒）
   */
  update(delta) {
    // 根据第一人称模式调整控制器参数
    if (this.params.firstPerson) {
      this.controls.maxPolarAngle = Math.PI;    // 允许完全上下观察
      this.controls.minDistance = 1e-4;         // 最小距离接近0
      this.controls.maxDistance = 1e-4;        // 锁定相机距离
    } else {
      this.controls.maxPolarAngle = Math.PI / 2; // 限制俯仰角度
      this.controls.minDistance = 1;            // 最小距离
      this.controls.maxDistance = 20;           // 最大距离
    }

    // 如果有碰撞体
    if (this.collider) {
      // 根据参数显示/隐藏调试元素
      this.collider.visible = this.params.displayCollider;
      if (this.visualizer) {
        this.visualizer.visible = this.params.displayBVH;
      }

      // 分步更新物理（提高稳定性）
      const physicsSteps = this.params.physicsSteps;
      for (let i = 0; i < physicsSteps; i++) {
        this.updatePlayer(delta / physicsSteps);
      }
    }
    
    // 更新控制器状态
    this.controls.update();
  }

  /**
   * 更新玩家物理状态
   * @param {number} delta 帧时间差（秒）
   */
  updatePlayer(delta) {
    // 应用重力加速度
    if (this.playerIsOnGround) {
      // 在地面上时应用基础重力
      this.playerVelocity.y = delta * this.params.gravity;
    } else {
      // 在空中时累积重力加速度
      this.playerVelocity.y += delta * this.params.gravity;
    }

    // 根据速度更新位置
    this.player.position.addScaledVector(this.playerVelocity, delta);

    // 基于相机方向的移动处理
    const angle = this.controls.getAzimuthalAngle(); // 获取相机水平角度

    // 前向移动（W键）
    if (this.fwdPressed) {
      this.tempVector.set(0, 0, -1)       // 初始前向向量
        .applyAxisAngle(this.upVector, angle) // 根据相机角度旋转
        .normalize();                     // 归一化
      this.player.position.addScaledVector(this.tempVector, this.params.playerSpeed * delta);
    }

    // 后向移动（S键）
    if (this.bkdPressed) {
      this.tempVector.set(0, 0, 1)        // 初始后向向量
        .applyAxisAngle(this.upVector, angle)
        .normalize();
      this.player.position.addScaledVector(this.tempVector, this.params.playerSpeed * delta);
    }

    // 左移（A键）
    if (this.lftPressed) {
      this.tempVector.set(-1, 0, 0)       // 初始左向向量
        .applyAxisAngle(this.upVector, angle)
        .normalize();
      this.player.position.addScaledVector(this.tempVector, this.params.playerSpeed * delta);
    }

    // 右移（D键）
    if (this.rgtPressed) {
      this.tempVector.set(1, 0, 0)        // 初始右向向量
        .applyAxisAngle(this.upVector, angle)
        .normalize();
      this.player.position.addScaledVector(this.tempVector, this.params.playerSpeed * delta);
    }

    // 更新玩家世界矩阵
    this.player.updateMatrixWorld();

    // 碰撞检测（如果有碰撞体）
    if (this.collider) {
      const capsuleInfo = this.player.capsuleInfo;

      // 准备临时变量
      this.tempBox.makeEmpty();                    // 清空包围盒
      this.tempMat.copy(this.collider.matrixWorld).invert(); // 获取碰撞体逆矩阵
      this.tempSegment.copy(capsuleInfo.segment);  // 复制胶囊线段

      // 将胶囊线段转换到碰撞体局部空间
      this.tempSegment.start.applyMatrix4(this.player.matrixWorld).applyMatrix4(this.tempMat);
      this.tempSegment.end.applyMatrix4(this.player.matrixWorld).applyMatrix4(this.tempMat);

      // 计算胶囊的轴对齐包围盒(AABB)
      this.tempBox.expandByPoint(this.tempSegment.start);
      this.tempBox.expandByPoint(this.tempSegment.end);
      this.tempBox.min.addScalar(-capsuleInfo.radius); // 扩展半径
      this.tempBox.max.addScalar(capsuleInfo.radius);

      // 使用BVH进行碰撞检测
      this.collider.geometry.boundsTree.shapecast({
        // 包围盒相交检测
        intersectsBounds: box => box.intersectsBox(this.tempBox),

        // 三角形相交检测
        intersectsTriangle: tri => {
          const triPoint = this.tempVector;    // 三角形上最近点
          const capsulePoint = this.tempVector2; // 胶囊上最近点

          // 计算线段到三角形的最短距离
          const distance = tri.closestPointToSegment(this.tempSegment, triPoint, capsulePoint);

          // 如果距离小于胶囊半径，发生碰撞
          if (distance < capsuleInfo.radius) {
            const depth = capsuleInfo.radius - distance; // 穿透深度
            const direction = capsulePoint.sub(triPoint).normalize(); // 碰撞方向

            // 调整胶囊位置（推开）
            this.tempSegment.start.addScaledVector(direction, depth);
            this.tempSegment.end.addScaledVector(direction, depth);
          }
        }
      });

      // 获取碰撞调整后的位置（世界空间）
      const newPosition = this.tempVector;
      newPosition.copy(this.tempSegment.start).applyMatrix4(this.collider.matrixWorld);

      // 计算位置调整向量
      const deltaVector = this.tempVector2;
      deltaVector.subVectors(newPosition, this.player.position);

      // 判断是否在地面上（主要Y轴调整）
      this.playerIsOnGround = deltaVector.y > Math.abs(delta * this.playerVelocity.y * 0.25);

      // 计算调整距离（避免微小调整）
      const offset = Math.max(0.0, deltaVector.length() - 1e-5);
      deltaVector.normalize().multiplyScalar(offset);

      // 应用位置调整
      this.player.position.add(deltaVector);

      // 调整速度（碰撞响应）
      if (!this.playerIsOnGround) {
        deltaVector.normalize();
        // 消除速度在碰撞方向的分量
        this.playerVelocity.addScaledVector(deltaVector, -deltaVector.dot(this.playerVelocity));
      } else {
        // 在地面上时重置水平速度
        this.playerVelocity.set(0, 0, 0);
      }
    }

    // 更新相机位置（跟随玩家）
    this.camera.position.sub(this.controls.target);
    this.controls.target.copy(this.player.position);
    this.camera.position.add(this.player.position);

    // 坠落检测（Y < -15时重置位置）
    if (this.player.position.y < -50) {
      this.reset();
    }
  }

  /**
   * 清理资源
   */
  dispose() {
    // 移除事件监听
    window.removeEventListener('keydown', this._keyDownHandler);
    window.removeEventListener('keyup', this._keyUpHandler);

    // 清理玩家对象
    if (this.player) {
      this.scene.remove(this.player);
      this.player.geometry.dispose();
      this.player.material.dispose();
    }

    // 清理控制器
    if (this.controls) {
      this.controls.dispose();
    }

    // 清理碰撞体
    if (this.collider) {
      this.scene.remove(this.collider);
      this.collider.geometry.dispose();
      this.collider.material.dispose();
    }

    // 清理环境模型
    if (this.environment) {
      this.scene.remove(this.environment);
    }
  }
}